#include <stdio.h>
#include "esp_spiffs.h"
#include <esp_log.h>
#include <esp_heap_caps.h>
#include <string.h>
#include "my_spiffs.h"

SPI_FFS &SPIFFS = SPI_FFS::getInstance();
static const char *TAG = "SPIFFS";

SPI_FFS &SPI_FFS::getInstance()
{
    static SPI_FFS instance; // 静态局部变量，保证唯一性
    return instance;
}

void SPI_FFS::begin(const std::string &mount_point, const std::string &partition_label_name, uint8_t max_open_file_number)
{
    const esp_vfs_spiffs_conf_t cfg = {
        .base_path = mount_point.c_str(),
        .partition_label = partition_label_name.c_str(),
        .max_files = max_open_file_number,
        .format_if_mount_failed = false,

    };
    build_system_success = esp_vfs_spiffs_register(&cfg);
    if (build_system_success != ESP_OK)
    {
        printf("BUILD FAILD\n");
        if (build_system_success == ESP_FAIL)
            printf("Failed to mount or format filesystem\n");

        else if (build_system_success == ESP_ERR_NOT_FOUND)
            printf("Failed to find SPIFFS partition\n");

        else
            printf("Failed to initialize SPIFFS (%s)\n", esp_err_to_name(build_system_success));

        return;
    }
    sucess_begin = true;
}

FILE *SPI_FFS::open(std::string path_to_open, std::string open_type) // type can be "r" or "w" or "a"
{
    if (build_system_success != ESP_OK)
    {
        ESP_LOGE(TAG, "NOT SUCCESS TO BEGIN SPIFFS");
        return nullptr;
    }

    std::string path = mount_point + path_to_open;
    FILE *file = fopen(path.c_str(), open_type.c_str());
    if (!file)
        ESP_LOGW(TAG, "Failed to open %s for %s", path.c_str(), open_type.c_str());

    return file;
}

void SPI_FFS::readToBuffer(FILE *f, void *buffer, size_t size)
{
    if (size > getSize(f))
    {
        ESP_LOGE(TAG, "Buffer size is too small");
        return;
    }
    size_t size_read = fread(buffer, 1, size, f);
    if (size_read != size)
    {
        ESP_LOGE(TAG, "Failed to read file for %d", size);
    }
}
std::string SPI_FFS::read(FILE *file)
{
    if (file == nullptr)
    {
        return ""; // 文件指针为空时返回空字符串
    }

    // 移动到文件末尾以确定文件大小
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    rewind(file); // 回到文件的起始位置

    if (file_size <= 0)
    {
        return ""; // 文件为空，直接返回空字符串
    }

    // 分配内存来存储文件内容
    void *buffer = heap_caps_malloc(file_size, MALLOC_CAP_DEFAULT);
    if (buffer == nullptr)
    {
        ESP_LOGE(TAG, "Failed to allocate memory at %s at line %d", __func__, __LINE__);
        return "";
    }

    // 使用 fread 读取整个文件内容到字符串的内存中
    size_t read_size = fread(buffer, 1, file_size, file);
    if (read_size != file_size)
    {
        ESP_LOGE(TAG, "Failed to read file");
        heap_caps_free(buffer);
        return "";
    }

    std::string result = std::string(static_cast<char *>(buffer), file_size);
    heap_caps_free(buffer);

    return result;
}
std::string SPI_FFS::read(FILE *file, size_t size)
{
    if (file == nullptr)
    {
        return ""; // 文件指针为空时返回空字符串
    }
    std::string result;
    result.reserve(size);
    size_t read_size = fread(&result[0], 1, size, file);
    if (read_size != size)
    {
        ESP_LOGW(TAG, "Failed to read file for %d", size);
    }

    return result;
}
std::string SPI_FFS::read(const std::string &path)
{
    FILE *file = open(path, "r");
    if (file == nullptr)
    {
        return ""; // 文件指针为空时返回空字符串
    }
    std::string result = read(file);
    closeFile(file);
    return result;
}
esp_err_t SPI_FFS::write(FILE *file, const char *data)
{
    if (file == nullptr)
        return ESP_FAIL; // 文件指针为空时返回错误

    size_t length = strlen(data);
    // 使用 fwrite 写入整个字符串到文件中
    size_t write_size = fwrite(data, 1, length, file);
    if (write_size != length)
    {
        printf("error writing files");
        return ESP_FAIL;
    }
    return ESP_OK;
}

esp_err_t SPI_FFS::write(FILE *file, const std::string &data)
{
    return write(file, data.c_str());
}

esp_err_t SPI_FFS::remove(const std::string &path)
{
    if (build_system_success != ESP_OK)
    {
        return ESP_FAIL;
    }

    if (::remove(std::string(mount_point + path).c_str()) != 0)
    {
        return ESP_FAIL;
    }
    return ESP_OK;
}

esp_err_t SPI_FFS::rename(const std::string &old_path, const std::string &new_path)
{
    if (build_system_success != ESP_OK)
    {
        return ESP_FAIL;
    }
    if (::rename(std::string(mount_point + old_path).c_str(), std::string(mount_point + new_path).c_str()) != 0)
    {
        return ESP_FAIL;
    }
    return ESP_OK;
}
size_t SPI_FFS::getSize(FILE *f)
{
    if (f == nullptr)
        return 0;
    fseek(f, 0, SEEK_END);
    size_t size = ftell(f);
    rewind(f);
    return size;
}

void SPI_FFS::closeFile(FILE *file)
{
    if (file == nullptr)
        return; // 文件指针为空时直接返回

    fclose(file);
}
void SPI_FFS::closeFileAndSpiffsAll(FILE *file)
{
    if (file != nullptr)
        fclose(file);
    esp_vfs_spiffs_unregister(NULL);
    printf("spiffs unregister\n");
}

SPI_FFS::SPI_FFS()
{
    build_system_success = ESP_FAIL;
}

bool SPI_FFS ::operator!()
{
    return this->build_system_success != ESP_OK || !sucess_begin;
}
DIR *SPI_FFS::openDir(const std::string &path)
{
    if (build_system_success != ESP_OK)
        return nullptr;
    std::string path_to_open = mount_point + path;
    DIR *dir = opendir(path_to_open.c_str());
    if (!dir)
    {
        ESP_LOGE(TAG, "Failed to open dir %s", path_to_open.c_str());
    }
    return dir;
}

uint16_t SPI_FFS::getDirFileCount(const std::string &dir_name)
{
    DIR *dir_t1 = openDir(dir_name);
    if (!dir_t1)
    {
        ESP_LOGE(TAG, "Failed to open dir %s", dir_name.c_str());
        return 0;
    }

    int count = 0;
    while (1)
    {
        struct dirent *de = readdir(dir_t1);
        if (!de)
        {
            break;
        }
        ++count;
    }
    return count;
}
